#include "parser.h"

/**
 * @brief 处理文件操作失败的情况
 * 
 * @param filename 文件路径
 * @return int 
 */
int handle_file_operate_error(char *filename)
{
    printf("Open file: %s failed!\n", filename);
    return -1;
}

/**
 * @brief 填充原始数据文件
 * 
 * @param filename 文件路径
 * @return int 
 */
int make_source_text(char *filename)
{
    FILE *fp = NULL;
    int column = 0;
    if (NULL == (fp = fopen(filename, "w")))
        return handle_file_operate_error(filename);
    for (column = 0; column < 3; ++column)
        fprintf(fp, "%d\r\n", column);
    fclose(fp);
}

/**
 * @brief 测试打开文件功能
 * 
 * @param filename 文件路径
 * @return int 
 */
int test_open_file(char *filename)
{
    FILE *fp = NULL;
    if (NULL == (fp = fopen(filename, "r")))
        return handle_file_operate_error(filename);

    printf("Open file: %s successed, fp = %p\n", filename, fp);
    fclose(fp);
    return 0;
}

/**
 * @brief 读取文件行到buf，按字符读取
 * 
 * @param fp 文件指针
 * @param strbuf 目的缓冲区
 * @param len 字符长度
 * @return int 
 */
int file_read_line_text(char *strbuf, int len, FILE *fp)
{
    if (NULL == fp)
    {
        printf("File pointer is NULL\r\n");
        return -1;
    }
    if (NULL == fgets(strbuf, len, fp)) // EOF
        return 1;
    return 0;
}

/**
 * @brief 从strbuf中写len个字节到文件，按字符写入
 * 
 * @param strbuf 缓冲区
 * @param len 字符数目
 * @param fp 文件指针
 * @return int 
 */
int file_write_line_text(char *strbuf, int len, FILE *fp)
{
    char *tmp = malloc(len + 1);
    if (NULL == fp || NULL == tmp)
    {
        printf("File pointer is NULL\r\n");
        return -1;
    }
    if ('\0' != strbuf[len - 1])
    {
        memcpy(tmp, strbuf, len);
        tmp[len] = '\0';
        fprintf(fp, tmp);
    }
    else
        fprintf(fp, strbuf);
    free(tmp);
    return 0;
}

/**
 * @brief 
 * 
 * @param data 读取len个字节二进制数据到data
 * @param len 读取数据块大小，单位字节
 * @param fp 文件指针
 * @return int 
 */
int file_read_binary(void *data, int len, FILE *fp)
{
    if (NULL == fp)
    {
        printf("File pointer is NULL\r\n");
        return -1;
    }
    if (0 == fread(data, 1, len, fp)) // EOF
        return 1;
    return 0;
}

/**
 * @brief 
 * 
 * @param data 从data写入len个二进制数据到fp
 * @param len 写入数据块大小，单位字节
 * @param fp 文件指针
 * @return int 
 */
int file_write_binary(const void *data, int len, FILE *fp)
{
    if (NULL == fp)
    {
        printf("File pointer is NULL\r\n");
        return -1;
    }
    fwrite(data, 1, len, fp); // 1:sizeof(unsigned char)
    return 0;
}

/**
 * @brief 测试文件字符写入以及读取
 * 
 * @return int 
 */
int test_rw_file_text()
{
    char *filename = "./source.txt";
    char strbuf[100];
    int sof_buf;
    FILE *fp = NULL;

    sof_buf = sizeof(strbuf);

    if (make_source_text(filename))
        return handle_file_operate_error(filename);

    if (NULL == (fp = fopen(filename, "r")))
        return handle_file_operate_error(filename);

    while (1)
    {
        if (file_read_line_text(strbuf, sof_buf, fp))
            break;
        printf("%s", strbuf);
    }

    fclose(fp);
    return 0;
}

int test_file_write_binary()
{
    char *filename = "./source.txt";
    FILE *fp = NULL;
    Girls g1, g2;

    if (NULL == (fp = fopen(filename, "w")))
        return handle_file_operate_error(filename);

    strcpy(g1.name, "Nakeko");
    g1.age = 24;
    g1.height = 170.0f;
    strcpy(g2.name, "慧子");
    g2.age = 24;
    g2.height = 171.0f;

    fwrite(&g1, sizeof(g1), 1, fp);
    fwrite(&g2, sizeof(g2), 1, fp);

    fclose(fp);
    return 0;
}

int test_file_read_binary()
{
    char *filename = "./source.txt";
    FILE *fp = NULL;
    Girls girl;

    fp = fopen(filename, "rb");
    assert(fp);

    while (1)
    {
        if (file_read_binary(&girl, sizeof(girl), fp))
            break;
        printf("Name:%s, age:%d, height:%.3fcm\r\n", girl.name, girl.age, girl.height);
    }

    fclose(fp);
    return 0;
}

int main()
{
    // test_file_write_binary();
    // test_file_read_binary();

    test_dict_file();

    return 0;
}

ParserFile *parser_read_init(const char *filename)
{
    ParserFile *file = NULL;
    FILE *fp = NULL;
    if (NULL == filename || 0 == strcmp(filename, ""))
        return NULL;
    fp = fopen(filename, "r");
    file = malloc(sizeof(ParserFile));
    file->filename = strdup(filename);
    file->fp = fp;
    file->line_number = 0;
    return file;
}

void dict_data_init(DictData *data, unsigned char fsmNo, unsigned char fsmState, unsigned char *pText)
{
    data->fsmNo = fsmNo;
    data->fsmState = fsmState;
    memcpy(data->text, pText, strlen(pText) + 1);
}

int dict_file_creat(char *filename)
{
    FILE *fp = NULL;
    DictData node;
    unsigned char i;
    unsigned char pucBuff[100];

    unsigned char *pTextCc[kCcBacked + 1] = {
        "气缸推出动作",
        "气缸到位检测",
        "气缸推出到位",
        "气缸复位动作",
        "气缸原位检测",
        "气缸复位原位",
    };

    unsigned char *pTextRs[kRsExit + 1] = {
        "初始空闲状态，在这个状态响应启动键，用于工件或双启动模式",
        "夹紧气缸夹紧状态",
        "运行判断状态，判断按下运行是直接运行还是Y轴到加工位",
        "Y轴就绪，移动到加工位",
        "等待另一轴加工完，然后启动当前轴，死机状态，由外部进行推进",
        "当前轴进入运行状态，死机状态，由外部进行推进",
        "当前Y轴运行结束，Y轴到下料位就绪",
        "夹紧气缸松开状态",
        "双启动UV处理",
        "等待双启动返回按钮",
        "判断Y轴运行结束需要回到哪个状态",
        "运行结束状态，进入此状态即退出运行状态，用于标准模式",
    };

    unsigned char *pTextWd[kWdTakedone + 1] = {
        "放料检测",
        "放料延时",
        "放料完成",
        "取料检测",
        "取料延时",
        "取料完成",
    };

    unsigned char *pTextUv[kUvpUvHold + 1] = {
        "阻塞状态",
        "入口状态",
        "下压气缸动作",
        "气缸下压到位后UV端口持续输出",
    };

    if (NULL == filename || 0 == strcmp(filename, ""))
        return -1;
    fp = fopen(filename, "w");
    assert(fp);

    // 夹紧气缸状态机
    for (i = kCcPush; i <= kCcBacked; ++i)
    {
        sprintf(pucBuff, "%s%s", "Y1-夹紧气缸-", pTextCc[i]);
        dict_data_init(&node, kFsmCcY1, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
        sprintf(pucBuff, "%s%s", "Y1-夹紧气缸-", pTextCc[i]);
        dict_data_init(&node, kFsmCcY2, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
    }

    // 运行状态机
    for (i = kRsInit; i <= kRsExit; ++i)
    {
        sprintf(pucBuff, "%s%s", "Y1-运行状态-", pTextRs[i]);
        dict_data_init(&node, kFsmRsY1, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
        sprintf(pucBuff, "%s%s", "Y1-运行状态-", pTextRs[i]);
        dict_data_init(&node, kFsmRsY2, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
    }

    // 工件检测状态机
    for (i = kWdPutDetect; i <= kWdTakedone; ++i)
    {
        sprintf(pucBuff, "%s%s", "Y1-工件状态-", pTextWd[i]);
        dict_data_init(&node, kFsmWdY1, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
        sprintf(pucBuff, "%s%s", "Y1-工件状态-", pTextWd[i]);
        dict_data_init(&node, kFsmWdY2, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
    }

    // UV流程状态机
    for (i = kUvpInit; i <= kUvpUvHold; ++i)
    {
        sprintf(pucBuff, "%s%s", "Y1-UV状态-", pTextUv[i]);
        dict_data_init(&node, kFsmUvY1, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
        sprintf(pucBuff, "%s%s", "Y1-UV状态-", pTextUv[i]);
        dict_data_init(&node, kFsmUvY2, i, pucBuff);
        fwrite(&node, sizeof(DictData), 1, fp);
    }

    fclose(fp);
}

/**
 * @brief 
 * 
 * @param filename 读取字典文件的内容到链表中
 * @param list 文件路径
 * @return int 链表指针
 */
int dict_file_read(char *filename, lsList *list)
{
    FILE *fp = NULL;
    DictData node;

    if (NULL == filename || 0 == strcmp(filename, ""))
        return -1;
    fp = fopen(filename, "rb");
    assert(fp);

    while (1)
    {
        if (feof(fp))
            break;
        fread(&node, sizeof(DictData), 1, fp);
        ls_list_append(list, &node);
    }
    fclose(fp);
}

int test_dict_file()
{
    char *filename = "./state.dict";
    FILE *fp = NULL;
    lsList *list = NULL;
    DictData node;
    Girls girl;

    dict_file_creat(filename);
    return 0;
    //..ls_list_init(&list, sizeof(DictData));
}

/**
 * @brief 读取字典文件，将字典存储到链表上，然后解析原始数据文件，并将组合的数据存储到新的文件中
 * 
 * @param filename 
 * @return int 
 */
int parser_file_read(char *filename)
{
    ParserFile *file = NULL;

    file = parser_read_init(filename);
    assert(file);
}

/******************************************************************************
*                                                                             *
* :: 链表模板类                                                                *
*                                                                             *
******************************************************************************/

/**
 * @brief 初始化外部链表指针，让其指向头节点
 * 
 * @param root 指向头节点指针的指针
 * @param nodeSize 链表存储数据的大小
 */
void ls_list_init(lsList **root, size_t nodeSize)
{
    lsList *list = NULL;
    list = (lsList *)malloc(sizeof(lsList));
    if (NULL == list || NULL == root)
        return;
    list->head = NULL;
    list->tail = NULL;
    list->curr = NULL;
    list->nodeSize = nodeSize;
    list->nodeNum = 0;
    *root = list;
}

/**
 * @brief 尾插式链表节点插入
 * 
 * @param list 链表头节点指针
 * @param data 节点数据域指针
 */
void ls_list_append(lsList *list, void *data)
{
    lsListNode *node = NULL;

    node = (lsListNode *)malloc(sizeof(lsListNode));
    if (NULL == node || NULL == list)
        return;
    node->data = data;
    node->next = NULL;
    if (NULL == list->head)
    {
        list->head = node;
        list->curr = node;
    }
    else
        list->tail->next = node;
    list->tail = node;
    list->nodeNum++;
}

/**
 * @brief 头插式链表节点插入
 * 
 * @param list 链表头节点指针
 * @param data 节点数据域指针
 */
void ls_list_prepend(lsList *list, void *data)
{
    lsListNode *node = NULL;

    node = (lsListNode *)malloc(sizeof(lsListNode));
    if (NULL == node || NULL == list)
        return;
    node->data = data;
    node->next = NULL;
    if (NULL == list->head)
    {
        list->head = node;
        list->tail = node;
        list->curr = node;
    }
    else
    {
        node->next = list->head->next;
        list->head = node;
    }
    list->nodeNum++;
}

/**
 * @brief 获取链表节点数量
 * 
 * @param list 链表头节点指针
 * @return size_t 
 */
size_t ls_list_node_num(lsList *list)
{
    return list->nodeNum;
}

/**
 * @brief 获取索引index处的节点数据
 * 
 * @param list 链表头节点指针
 * @param element 数据指针，一般为外部变量指针
 * @param index 索引
 * @return size_t 0-操作成功 1-操作失败
 */
size_t ls_list_get_element_entity(lsList *list, void *element, size_t index)
{
    size_t i = 0;
    lsListNode *node;

    if (NULL == list || index >= ls_list_node_num(list))
        return 1;
    node = list->head;
    while (node)
    {
        if (i == index)
            break;
        node = node->next;
        ++i;
    }
    memcpy(element, node->data, list->nodeSize);
    return 0;
}

/**
 * @brief 获取索引index处的节点数据指针
 * 
 * @param list 链表头节点指针
 * @param index 索引
 * @return size_t NULL-操作失败
 */
void *ls_list_get_element_pointer(lsList *list, size_t index)
{
    size_t i = 0;
    lsListNode *node;

    if (NULL == list || index >= ls_list_node_num(list))
        return NULL;
    node = list->head;
    while (node)
    {
        if (i == index)
            break;
        node = node->next;
        ++i;
    }
    return node->data;
}

/**
 * @brief 设置curr指针指向索引为index处的节点
 * 
 * @param list 链表头节点指针
 * @param index 索引
 * @return size_t 0-操作成功 1-操作失败
 */
size_t ls_list_set_curr_node(lsList *list, size_t index)
{
    if (NULL == list || index >= ls_list_node_num(list))
        return 1;
    list->curr = list->head;
    while ((index--) >= 1)
    {
        list->curr = list->curr->next;
    }
    return 0;
}

/**
 * @brief 获取curr指针指向的节点数据指针
 * 
 * @param list 链表头节点指针
 * @return void* NULL-操作失败
 */
void *ls_list_get_curr_element(lsList *list)
{
    if (NULL == list)
        return NULL;
    return list->curr->data;
}

/**
 * @brief 链表curr指针指向下一个节点，如果到达最后一个节点，指向第一个节点
 * 
 * @param list 
 * @return size_t 
 */
size_t ls_list_curr_next(lsList *list)
{
    if (NULL == list)
        return 1;
    if (NULL == list->curr->next)
        list->curr = list->head;
    else
        list->curr = list->curr->next;
    return 0;
}

/**
 * @brief 释放链表
 * 
 * @param root 指向头节点指针的指针
 */
void ls_list_free(lsList **root)
{
    lsListNode *node;
    lsListNode *head;

    if (NULL == root || NULL == *root)
        return;

    head = (*root)->head; // 指向第一个节点
    while (head)
    {
        node = head;
        head = head->next;
        free(node);
    }
    free(*root);
    *root = NULL;
}